<?php


namespace PKFrame\Lib;
defined('PATH_PK') or die();

use PKFrame\DataHandler\Arrays;
use PKFrame\DataHandler\JSON;

class Curl
{
    private $_url;
    private $_isSSI;
    private $_is_SSLVersion = true;
    private $_param, $_paramByHead;
    private $_method;
    private $_timeOut = 0;
    private $_init;
    private $_response;

    public function __construct($url, $method)
    {
        function_exists('curl_init') ?: $this->_notice('php extend curl no exists');
        $this->_url = $url;
        $this->_isSSI = stristr($url, 'https');
        $this->_method = $method;
    }

    private function _notice($str)
    {
        out()->noticeByJson($str);
    }

    public function Param($param = []): Curl
    {
        switch (strtolower($this->_method)) {
            case 'get':
            case 'file':
                $this->_url .= '?' . Arrays::ToQuery($param);
                break;
            case 'post':
                $this->_param = Arrays::ToQuery($param);
                break;
            case 'json':
                $this->_param = JSON::EnCode($param);
                break;
        }
        return $this;
    }

    public function setParamsByHead(array $params = []): Curl
    {
        $this->_paramByHead = $params;
        return $this;
    }

    /**
     * 超时
     * @param int $second
     * @return $this
     */
    public function TimeOut($second = 0): Curl
    {
        $this->_timeOut = $second;
        return $this;
    }

    public function OutPutParam()
    {
        exit(var_dump($this));
    }

    public function Request()
    {
        $this->_init = curl_init();
        // 设置超时限制防止死循环
        if ($this->_timeOut > 0) {
            curl_setopt($this->_init, CURLOPT_TIMEOUT, $this->_timeOut);
            curl_setopt($this->_init, CURLOPT_CONNECTTIMEOUT, $this->_timeOut);
        }
        $actionName = 'request' . ucfirst($this->_method);
        try {
            if (method_exists($this, $actionName)) {
                if (version_compare(phpversion(), '7.0.0', '>=')) {
                    $this->{$actionName}();
                } else {
                    $this->$actionName();
                }
            } else {
                throw new \Exception('CURL class no Exists Function name: ' . $actionName);
            }
        } catch (\Exception $exception) {
            handlerException($exception);
        }
        if ($actionName != 'requestFILE') {
            curl_setopt($this->_init, CURLOPT_URL, $this->_url);

            if (version_compare(PHP_VERSION, '5.5.0', '>')) {
                $this->_multi();
            } else {
                $this->_exec();
            }
        }
        return $this->_response;
    }

    public function setIsSSLVERSION($is_version)
    {
        $this->_is_SSLVersion = $is_version;
    }

    private function _common()
    {
        curl_setopt($this->_init, CURLOPT_HEADER, FALSE);
        curl_setopt($this->_init, CURLOPT_RETURNTRANSFER, TRUE);
        curl_setopt($this->_init, CURLOPT_FAILONERROR, true);
    }

    protected function requestGET()
    {
        $this->_common();
        if ($this->_isSSI) {
            curl_setopt($this->_init, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($this->_init, CURLOPT_SSL_VERIFYHOST, FALSE);
        }
    }

    protected function requestPOST()
    {
        if ($this->_isSSI) {
            curl_setopt($this->_init, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($this->_init, CURLOPT_SSL_VERIFYHOST, FALSE);
            if ($this->_is_SSLVersion) {
                //CURL_SSLVERSION_TLSv1
                curl_setopt($this->_init, CURLOPT_SSLVERSION, TRUE);
            }
        }
        self::_common();
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            curl_setopt($this->_init, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
        }
        curl_setopt($this->_init, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($this->_init, CURLOPT_AUTOREFERER, 1);
        !array_key_exists('cookie_file', $GLOBALS) ?:
            curl_setopt($this->_init, CURLOPT_COOKIEFILE, $GLOBALS['cookie_file']);
        curl_setopt($this->_init, CURLOPT_POST, true);
        curl_setopt($this->_init, CURLOPT_POSTFIELDS, $this->_param);
    }

    protected function requestJSON()
    {
        if ($this->_isSSI) {
            curl_setopt($this->_init, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($this->_init, CURLOPT_SSL_VERIFYHOST, TRUE);
            if ($this->_is_SSLVersion) {
                //CURL_SSLVERSION_TLSv1
                curl_setopt($this->_init, CURLOPT_SSLVERSION, TRUE);
            }
        }
        $this->_common();
        curl_setopt($this->_init, CURLOPT_POST, true);
        curl_setopt($this->_init, CURLOPT_POSTFIELDS, $this->_param);
        curl_setopt($this->_init, CURLOPT_VERBOSE, 1);
        curl_setopt($this->_init, CURLOPT_RETURNTRANSFER, true);
        $params_head = [
            'Content-Type: application/json',
            'Content-Length: ' . strlen($this->_param)
        ];
        if (Arrays::Is($this->_paramByHead)) {
            $params_head = array_merge($params_head, $this->_paramByHead);
        }
        curl_setopt($this->_init, CURLOPT_HTTPHEADER, $params_head);
    }

    protected function requestFILE()
    {
        curl_setopt($this->_init, CURLOPT_URL, $this->_url);
        curl_setopt($this->_init, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($this->_init, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($this->_init, CURLOPT_RETURNTRANSFER, 1);
        $this->_response = curl_exec($this->_init); // 已经获取到内容，没有输出到页面上。
        curl_close($this->_init);
    }

    /**
     * 只支持 5.5.0 以上的版本(批处理)
     */
    private function _multi()
    {
        try {
            $handle = curl_multi_init();
            curl_multi_add_handle($handle, $this->_init);
            $flag = null;
            do {
                $status = curl_multi_exec($handle, $flag);
                // Check for errors
                if ($status > 0) {
                    // Display error message
                    throw new \Exception('curl exec error:' . curl_multi_strerror($status));
                }
            } while ($flag > 0);
            if (curl_errno($this->_init) > 0) {
                throw new \Exception(curl_error($this->_init));
            }
            if (curl_getinfo($this->_init, CURLINFO_HTTP_CODE) !== 200) {
                $msg = 'CURL HTTP State Code: ' . curl_getinfo($this->_init, CURLINFO_HTTP_CODE)
                    . ', Msg:' . curl_error($this->_init);
                throw new \Exception($msg);
            }
            $this->_response = curl_multi_getcontent($this->_init);
            $this->_logRequestTime();
            curl_multi_remove_handle($handle, $this->_init);
            curl_multi_close($handle);
        } catch (\Exception $exception) {
            $this->_notice($exception->getMessage());
        }
    }

    private function _exec()
    {
        try {
            $this->_response = curl_exec($this->_init);
            if (curl_errno($this->_init) > 0) {
                throw new \Exception(curl_error($this->_init));
            }
            if (curl_getinfo($this->_init, CURLINFO_HTTP_CODE) !== 200) {
                $msg = 'CURL HTTP State Code: ' . curl_getinfo($this->_init, CURLINFO_HTTP_CODE)
                    . ', Msg:' . curl_error($this->_init);
                throw new \Exception($msg);
            }
            $this->_logRequestTime();
            curl_close($this->_init);
        } catch (\Exception $exception) {
            $this->_notice($exception->getMessage());
        }
    }

    private function _logRequestTime()
    {
        /**
         * 总共的传输时间（total_time），获得用秒表示的上一次传输总共的时间，包括DNS解析、TCP连接等。
         * 直到DNS解析完成时间（namelookup_time），获得用秒表示的从最开始到域名解析完毕的时间。
         * 建立连接时间（connect_time），获得用秒表示的从最开始直到对远程主机（或代理）的连接完毕的时间。
         * 传输前耗时（pretransfer_time），获得用秒表示的从最开始直到文件刚刚开始传输的时间。
         * 开始传输（starttransfer_time），获得用秒表示的从最开始到第一个字节被curl收到的时间。
         * 重定向时间（redirect_time），获得所有用秒表示的包含了所有重定向步骤的时间，包括DNS解析、连接、传输前（pretransfer)和在最后的一次传输开始之前。
         */
        $curl_info = curl_getinfo($this->_init);
        $msg = ['total time:' . $curl_info['total_time'] . 's',
            'dns time:' . $curl_info['namelookup_time'] . 's',
            'connect time:' . $curl_info['connect_time'] . 's',
            'pretransfer time:' . $curl_info['pretransfer_time'] . 's',
            'starttransfer time:' . $curl_info['starttransfer_time'] . 's',
            'redirect time:' . $curl_info['redirect_time'] . 's',];
        logger('LogCUrl')->LOGS($this->_url, $msg);
    }
}
